
/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 *版权所有2002-2022原作者。
 *
 *根据Apache许可证2.0版（“许可证”）许可；
 *除非符合许可证的规定，否则您不得使用此文件。
 *您可以在以下网址获取许可证副本
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *除非适用法律要求或书面同意，否则软件
 *根据许可证分发是在“按原样”的基础上分发的，
 *无任何明示或暗示的保证或条件。
 *有关管理权限的特定语言，请参阅许可证
 *许可证下的限制。
 */

package org.springframework.context.support;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

import org.springframework.context.HierarchicalMessageSource;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.NoSuchMessageException;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

/**
 * Abstract implementation of the {@link HierarchicalMessageSource} interface,
 * implementing common handling of message variants, making it easy
 * to implement a specific strategy for a concrete MessageSource.
 *
 * <p>Subclasses must implement the abstract {@link #resolveCode}
 * method. For efficient resolution of messages without arguments, the
 * {@link #resolveCodeWithoutArguments} method should be overridden
 * as well, resolving messages without a MessageFormat being involved.
 *
 * <p><b>Note:</b> By default, message texts are only parsed through
 * MessageFormat if arguments have been passed in for the message. In case
 * of no arguments, message texts will be returned as-is. As a consequence,
 * you should only use MessageFormat escaping for messages with actual
 * arguments, and keep all other messages unescaped. If you prefer to
 * escape all messages, set the "alwaysUseMessageFormat" flag to "true".
 *
 * <p>Supports not only MessageSourceResolvables as primary messages
 * but also resolution of message arguments that are in turn
 * MessageSourceResolvables themselves.
 *
 * <p>This class does not implement caching of messages per code, thus
 * subclasses can dynamically change messages over time. Subclasses are
 * encouraged to cache their messages in a modification-aware fashion,
 * allowing for hot deployment of updated messages.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @see #resolveCode(String, java.util.Locale)
 * @see #resolveCodeWithoutArguments(String, java.util.Locale)
 * @see #setAlwaysUseMessageFormat
 * @see java.text.MessageFormat
 */
/**
 *{@link HierarchicalMessageSource}接口的抽象实现，
 *实现消息变体的通用处理，使其变得简单
 *以实现针对具体MessageSource的特定策略。
 *
 *＜p＞子类必须实现抽象｛@link#resolveCode｝
 *方法。为了在没有参数的情况下高效地解决消息
 *｛@link#resolveCodeWithoutArguments｝方法应被重写
 *以及在不涉及MessageFormat的情况下解析消息。
 *
 *<p><b>注意：</b>默认情况下，消息文本仅通过
 *如果已为消息传入参数，则设置MessageFormat。万一
 *如果没有参数，消息文本将按原样返回。因此，
 *您应该只对具有实际值的消息使用MessageFormat转义
 *参数，并保持所有其他消息不被跳过。如果你愿意
 *转义所有消息，将“alwaysUseMessageFormat”标志设置为“true”。
 *
 *<p>不仅支持MessageSourceResolutible作为主消息
 *还可以解决消息参数
 *MessageSourceResolutible本身。
 *
 *<p>此类不实现每个代码的消息缓存，因此
 *子类可以随时间动态地更改消息。子类是
 *鼓励以知晓修改的方式缓存它们的消息，
 *从而允许对更新的消息进行热部署。
 *
 *@作者Juergen Hoeller
 *@作者罗德·约翰逊
 *@见#resolveCode（字符串，java.util.Locale）
 *@见#resolveCodeWithoutArguments（字符串，java.util.Locate）
 *@参见#setAlwaysUseMessageFormat
 *@参见java.text.MessageFormat
 */
public abstract class AbstractMessageSource extends MessageSourceSupport implements HierarchicalMessageSource {

	@Nullable
	private MessageSource parentMessageSource;

	@Nullable
	private Properties commonMessages;

	private boolean useCodeAsDefaultMessage = false;


	@Override
	public void setParentMessageSource(@Nullable MessageSource parent) {
		this.parentMessageSource = parent;
	}

	@Override
	@Nullable
	public MessageSource getParentMessageSource() {
		return this.parentMessageSource;
	}

	/**
	 * Specify locale-independent common messages, with the message code as key
	 * and the full message String (may contain argument placeholders) as value.
	 * <p>May also link to an externally defined Properties object, e.g. defined
	 * through a {@link org.springframework.beans.factory.config.PropertiesFactoryBean}.
	 */
	/**
	 *指定与区域设置无关的通用消息，以消息代码为键
	 *以及完整的消息String（可能包含参数占位符）作为值。
	 *<p>也可以链接到外部定义的Properties对象，例如定义的
	 *通过{@linkorg.springframework.beans.factory.config.PropertiesFactoryBean}。
	 */
	public void setCommonMessages(@Nullable Properties commonMessages) {
		this.commonMessages = commonMessages;
	}

	/**
	 * Return a Properties object defining locale-independent common messages, if any.
	 */
	/**
	 *返回一个Properties对象，该对象定义与区域设置无关的常见消息（如果有的话）。
	 */
	@Nullable
	protected Properties getCommonMessages() {
		return this.commonMessages;
	}

	/**
	 * Set whether to use the message code as default message instead of
	 * throwing a NoSuchMessageException. Useful for development and debugging.
	 * Default is "false".
	 * <p>Note: In case of a MessageSourceResolvable with multiple codes
	 * (like a FieldError) and a MessageSource that has a parent MessageSource,
	 * do <i>not</i> activate "useCodeAsDefaultMessage" in the <i>parent</i>:
	 * Else, you'll get the first code returned as message by the parent,
	 * without attempts to check further codes.
	 * <p>To be able to work with "useCodeAsDefaultMessage" turned on in the parent,
	 * AbstractMessageSource and AbstractApplicationContext contain special checks
	 * to delegate to the internal {@link #getMessageInternal} method if available.
	 * In general, it is recommended to just use "useCodeAsDefaultMessage" during
	 * development and not rely on it in production in the first place, though.
	 * @see #getMessage(String, Object[], Locale)
	 * @see org.springframework.validation.FieldError
	 */
	/**
	 *设置是否将消息代码用作默认消息，而不是
	 *引发NoSuchMessageException。对开发和调试很有用。
	 *默认值为“false”。
	 *<p>注意：如果MessageSourceResolvable具有多个代码
	 *（如FieldError）和具有父MessageSource的MessageSource，
	 *是否<i>不</i>激活<i>父级</i>中的“useCodeAsDefaultMessage”：
	 *否则，您将得到父级作为消息返回的第一个代码，
	 *而不尝试检查进一步的代码。
	 *<p>为了能够在父级中打开“useCodeAsDefaultMessage”的情况下工作，
	 *AbstractMessageSource和AbstractApplicationContext包含特殊检查
	 *委托给内部｛@link#getMessageInternal｝方法（如果可用）。
	 *通常，建议在
	 *开发，而不是在生产中首先依赖它。
	 *@see#getMessage（字符串，对象[]，区域设置）
	 *@参见org.springframework.validation.FieldError
	 */
	public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) {
		this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
	}

	/**
	 * Return whether to use the message code as default message instead of
	 * throwing a NoSuchMessageException. Useful for development and debugging.
	 * Default is "false".
	 * <p>Alternatively, consider overriding the {@link #getDefaultMessage}
	 * method to return a custom fallback message for an unresolvable code.
	 * @see #getDefaultMessage(String)
	 */
	/**
	 *返回是否将消息代码用作默认消息，而不是
	 *引发NoSuchMessageException。对开发和调试很有用。
	 *默认值为“false”。
	 *＜p＞或者，考虑重写｛@link#getDefaultMessage｝
	 *方法为无法解析的代码返回自定义回退消息。
	 *@see#getDefaultMessage（字符串）
	 */
	protected boolean isUseCodeAsDefaultMessage() {
		return this.useCodeAsDefaultMessage;
	}


	@Override
	public final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
		String msg = getMessageInternal(code, args, locale);
		if (msg != null) {
			return msg;
		}
		if (defaultMessage == null) {
			return getDefaultMessage(code);
		}
		return renderDefaultMessage(defaultMessage, args, locale);
	}

	@Override
	public final String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException {
		String msg = getMessageInternal(code, args, locale);
		if (msg != null) {
			return msg;
		}
		String fallback = getDefaultMessage(code);
		if (fallback != null) {
			return fallback;
		}
		throw new NoSuchMessageException(code, locale);
	}

	@Override
	public final String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
		String[] codes = resolvable.getCodes();
		if (codes != null) {
			for (String code : codes) {
				String message = getMessageInternal(code, resolvable.getArguments(), locale);
				if (message != null) {
					return message;
				}
			}
		}
		String defaultMessage = getDefaultMessage(resolvable, locale);
		if (defaultMessage != null) {
			return defaultMessage;
		}
		throw new NoSuchMessageException(!ObjectUtils.isEmpty(codes) ? codes[codes.length - 1] : "", locale);
	}


	/**
	 * Resolve the given code and arguments as message in the given Locale,
	 * returning {@code null} if not found. Does <i>not</i> fall back to
	 * the code as default message. Invoked by {@code getMessage} methods.
	 * @param code the code to lookup up, such as 'calculator.noRateSet'
	 * @param args array of arguments that will be filled in for params
	 * within the message
	 * @param locale the locale in which to do the lookup
	 * @return the resolved message, or {@code null} if not found
	 * @see #getMessage(String, Object[], String, Locale)
	 * @see #getMessage(String, Object[], Locale)
	 * @see #getMessage(MessageSourceResolvable, Locale)
	 * @see #setUseCodeAsDefaultMessage
	 */
	/**
	 *将给定的代码和参数解析为给定Locale中的消息，
	 *如果未找到，则返回｛@code null｝。<i>是否</i>回落到
	 *代码作为默认消息。由｛@code getMessage｝方法调用。
	 *@param编码要查找的代码，例如“calculator.noRateSet”
	 *@param args将为params填充的参数数组
	 *在消息中
	 *@param locale进行查找的区域设置
	 *@返回已解析的消息，如果未找到，则返回｛@code null｝
	 *@see#getMessage（字符串，对象[]，字符串，区域设置）
	 *@see#getMessage（字符串，对象[]，区域设置）
	 *@see#getMessage（消息源可解析，区域设置）
	 *@参见#setUseCodeAsDefaultMessage
	 */
	@Nullable
	protected String getMessageInternal(@Nullable String code, @Nullable Object[] args, @Nullable Locale locale) {
		if (code == null) {
			return null;
		}
		if (locale == null) {
			locale = Locale.getDefault();
		}
		Object[] argsToUse = args;

		if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
			// Optimized resolution: no arguments to apply,
			// 优化的解决方案：无需应用参数，
			// therefore no MessageFormat needs to be involved.
			// 因此不需要涉及MessageFormat。
			// Note that the default implementation still uses MessageFormat;
			// 请注意，默认实现仍然使用MessageFormat；
			// this can be overridden in specific subclasses.
			// 这可以在特定的子类中被覆盖。
			String message = resolveCodeWithoutArguments(code, locale);
			if (message != null) {
				return message;
			}
		}

		else {
			// Resolve arguments eagerly, for the case where the message
			// 急切地解决争论，对于消息
			// is defined in a parent MessageSource but resolvable arguments
			// 在父MessageSource中定义，但可解析参数
			// are defined in the child MessageSource.
			// 在子MessageSource中定义。
			argsToUse = resolveArguments(args, locale);

			MessageFormat messageFormat = resolveCode(code, locale);
			if (messageFormat != null) {
				synchronized (messageFormat) {
					return messageFormat.format(argsToUse);
				}
			}
		}

		// Check locale-independent common messages for the given message code.
		// 为给定的消息代码检查与区域设置无关的通用消息。
		Properties commonMessages = getCommonMessages();
		if (commonMessages != null) {
			String commonMessage = commonMessages.getProperty(code);
			if (commonMessage != null) {
				return formatMessage(commonMessage, args, locale);
			}
		}

		// Not found -> check parent, if any.
		// 未找到->检查父项（如果有）。
		return getMessageFromParent(code, argsToUse, locale);
	}

	/**
	 * Try to retrieve the given message from the parent {@code MessageSource}, if any.
	 * @param code the code to lookup up, such as 'calculator.noRateSet'
	 * @param args array of arguments that will be filled in for params
	 * within the message
	 * @param locale the locale in which to do the lookup
	 * @return the resolved message, or {@code null} if not found
	 * @see #getParentMessageSource()
	 */
	/**
	 *尝试从父｛@code MessageSource｝中检索给定的消息（如果有的话）。
	 *@param编码要查找的代码，例如“calculator.noRateSet”
	 *@param args将为params填充的参数数组
	 *在消息中
	 *@param locale进行查找的区域设置
	 *@返回已解析的消息，如果未找到，则返回｛@code null｝
	 *@参见#getParentMessageSource（）
	 */
	@Nullable
	protected String getMessageFromParent(String code, @Nullable Object[] args, Locale locale) {
		MessageSource parent = getParentMessageSource();
		if (parent != null) {
			if (parent instanceof AbstractMessageSource abstractMessageSource) {
				// Call internal method to avoid getting the default code back
				// 调用内部方法以避免返回默认代码
				// in case of "useCodeAsDefaultMessage" being activated.
				// 在“useCodeAsDefaultMessage”被激活的情况下。
				return abstractMessageSource.getMessageInternal(code, args, locale);
			}
			else {
				// Check parent MessageSource, returning null if not found there.
				// 检查父MessageSource，如果在那里找不到，则返回null。
				// Covers custom MessageSource impls and DelegatingMessageSource.
				// 涵盖自定义MessageSource impls和DelegatingMessageSource。
				return parent.getMessage(code, args, null, locale);
			}
		}
		// Not found in parent either.
		// 在父级中也找不到。
		return null;
	}

	/**
	 * Get a default message for the given {@code MessageSourceResolvable}.
	 * <p>This implementation fully renders the default message if available,
	 * or just returns the plain default message {@code String} if the primary
	 * message code is being used as a default message.
	 * @param resolvable the value object to resolve a default message for
	 * @param locale the current locale
	 * @return the default message, or {@code null} if none
	 * @since 4.3.6
	 * @see #renderDefaultMessage(String, Object[], Locale)
	 * @see #getDefaultMessage(String)
	 */
	/**
	 *获取给定｛@code MessageSourceResolutible｝的默认消息。
	 *<p>此实现完全呈现默认消息（如果可用），
	 *或者仅返回纯默认消息｛@code String｝，如果
	 *消息代码被用作默认消息。
	 *@param resolvable要为其解析默认消息的值对象
	 *@param locale当前区域设置
	 *@返回默认消息，如果没有，则返回｛@code null｝
	 *@自4.3.6起
	 *@see#renderDefaultMessage（字符串、对象[]、区域设置）
	 *@see#getDefaultMessage（字符串）
	 */
	@Nullable
	protected String getDefaultMessage(MessageSourceResolvable resolvable, Locale locale) {
		String defaultMessage = resolvable.getDefaultMessage();
		String[] codes = resolvable.getCodes();
		if (defaultMessage != null) {
			if (resolvable instanceof DefaultMessageSourceResolvable defaultMessageSourceResolvable &&
					!defaultMessageSourceResolvable.shouldRenderDefaultMessage()) {
				// Given default message does not contain any argument placeholders
				// 给定的默认消息不包含任何参数占位符
				// (and isn't escaped for alwaysUseMessageFormat either) -> return as-is.
				// （对于alwaysUseMessageFormat也没有转义）->按原样返回。
				return defaultMessage;
			}
			if (!ObjectUtils.isEmpty(codes) && defaultMessage.equals(codes[0])) {
				// Never format a code-as-default-message, even with alwaysUseMessageFormat=true
				// 永远不要将代码格式化为默认消息，即使alwaysUseMessageFormat=true
				return defaultMessage;
			}
			return renderDefaultMessage(defaultMessage, resolvable.getArguments(), locale);
		}
		return (!ObjectUtils.isEmpty(codes) ? getDefaultMessage(codes[0]) : null);
	}

	/**
	 * Return a fallback default message for the given code, if any.
	 * <p>Default is to return the code itself if "useCodeAsDefaultMessage" is activated,
	 * or return no fallback else. In case of no fallback, the caller will usually
	 * receive a {@code NoSuchMessageException} from {@code getMessage}.
	 * @param code the message code that we couldn't resolve
	 * and that we didn't receive an explicit default message for
	 * @return the default message to use, or {@code null} if none
	 * @see #setUseCodeAsDefaultMessage
	 */
	/**
	 *返回给定代码的回退默认消息（如果有的话）。
	 *<p>如果“useCodeAsDefaultMessage”被激活，则默认返回代码本身，
	 *否则不返回回退。在没有回退的情况下，调用者通常会
	 *从{@code getMessage｝接收{@code NoSuchMessageException｝。
	 *@param code我们无法解析的消息代码
	 *并且我们没有收到
	 *@返回要使用的默认消息，如果没有，则返回｛@code null｝
	 *@参见#setUseCodeAsDefaultMessage
	 */
	@Nullable
	protected String getDefaultMessage(String code) {
		if (isUseCodeAsDefaultMessage()) {
			return code;
		}
		return null;
	}


	/**
	 * Searches through the given array of objects, finds any MessageSourceResolvable
	 * objects and resolves them.
	 * <p>Allows for messages to have MessageSourceResolvables as arguments.
	 * @param args array of arguments for a message
	 * @param locale the locale to resolve through
	 * @return an array of arguments with any MessageSourceResolvables resolved
	 */
	/**
	 *搜索给定的对象数组，找到任何可解析的MessageSourceResolutible
	 *对象并解析它们。
	 *<p>允许消息将MessageSourceResolutibles作为参数。
	 *@param args消息的参数数组
	 *@param locale要解析的区域设置
	 *@返回已解析任何MessageSourceResolutibles的参数数组
	 */
	@Override
	protected Object[] resolveArguments(@Nullable Object[] args, Locale locale) {
		if (ObjectUtils.isEmpty(args)) {
			return super.resolveArguments(args, locale);
		}
		List<Object> resolvedArgs = new ArrayList<>(args.length);
		for (Object arg : args) {
			if (arg instanceof MessageSourceResolvable messageSourceResolvable) {
				resolvedArgs.add(getMessage(messageSourceResolvable, locale));
			}
			else {
				resolvedArgs.add(arg);
			}
		}
		return resolvedArgs.toArray();
	}

	/**
	 * Subclasses can override this method to resolve a message without arguments
	 * in an optimized fashion, i.e. to resolve without involving a MessageFormat.
	 * <p>The default implementation <i>does</i> use MessageFormat, through
	 * delegating to the {@link #resolveCode} method. Subclasses are encouraged
	 * to replace this with optimized resolution.
	 * <p>Unfortunately, {@code java.text.MessageFormat} is not implemented
	 * in an efficient fashion. In particular, it does not detect that a message
	 * pattern doesn't contain argument placeholders in the first place. Therefore,
	 * it is advisable to circumvent MessageFormat for messages without arguments.
	 * @param code the code of the message to resolve
	 * @param locale the locale to resolve the code for
	 * (subclasses are encouraged to support internationalization)
	 * @return the message String, or {@code null} if not found
	 * @see #resolveCode
	 * @see java.text.MessageFormat
	 */
	/**
	 *子类可以重写此方法以解析不带参数的消息
	 *以优化的方式，即在不涉及MessageFormat的情况下进行解析。
	 *<p>默认实现<i>确实</i>使用MessageFormat，通过
	 *委托给｛@link#resolveCode｝方法。鼓励使用子类
	 *将其替换为优化的分辨率。
	 *＜p＞不幸的是，｛@code java.text.MessageFormat｝未实现
	 *以有效的方式。特别是，它不会检测到
	 *模式一开始就不包含参数占位符。因此
	 *对于没有参数的消息，建议避开MessageFormat。
	 *@param code要解析的消息的代码
	 *@param locale用于解析代码的区域设置
	 *（鼓励子类支持国际化）
	 *@返回消息String，如果未找到，则返回｛@code null｝
	 *@参见#resolveCode
	 *@参见java.text.MessageFormat
	 */
	@Nullable
	protected String resolveCodeWithoutArguments(String code, Locale locale) {
		MessageFormat messageFormat = resolveCode(code, locale);
		if (messageFormat != null) {
			synchronized (messageFormat) {
				return messageFormat.format(new Object[0]);
			}
		}
		return null;
	}

	/**
	 * Subclasses must implement this method to resolve a message.
	 * <p>Returns a MessageFormat instance rather than a message String,
	 * to allow for appropriate caching of MessageFormats in subclasses.
	 * <p><b>Subclasses are encouraged to provide optimized resolution
	 * for messages without arguments, not involving MessageFormat.</b>
	 * See the {@link #resolveCodeWithoutArguments} javadoc for details.
	 * @param code the code of the message to resolve
	 * @param locale the locale to resolve the code for
	 * (subclasses are encouraged to support internationalization)
	 * @return the MessageFormat for the message, or {@code null} if not found
	 * @see #resolveCodeWithoutArguments(String, java.util.Locale)
	 */
	/**
	 *子类必须实现此方法才能解析消息。
	 *<p>返回MessageFormat实例，而不是消息字符串，
	 *以允许在子类中适当地缓存MessageFormats。
	 *<p><b>鼓励子类提供优化的解决方案
	 *对于没有参数的消息，不涉及MessageFormat</b>
	 *有关详细信息，请参阅{@link#resolveCodeWithoutArguments}javadoc。
	 *@param code要解析的消息的代码
	 *@param locale用于解析代码的区域设置
	 *（鼓励子类支持国际化）
	 *@return消息的MessageFormat，如果未找到，则返回｛@code null｝
	 *@见#resolveCodeWithoutArguments（字符串，java.util.Locate）
	 */
	@Nullable
	protected abstract MessageFormat resolveCode(String code, Locale locale);

}
